package util

import (
	"errors"
	"github.com/golang-jwt/jwt/v4"
	"github.com/lib/pq"
	"time"
)

// Auth 验证结构体，用于在Token中传递消息
type Auth struct {
	ID        int            `json:"id"`
	Username  string         `json:"username"`
	Password  string         `json:"password"`
	Name      string         `json:"name"`
	Power     int            `json:"power"`
	ChatNames pq.StringArray `json:"chat_names,omitempty" gorm:"type:varchar(255)[]"` // 用户所在聊天室的名字列表
}

// MyClaims JWT不可少的claims
type MyClaims struct {
	User Auth
	jwt.StandardClaims
}

const TokenExpireDuration = time.Hour * 2

// MySecret 我的签名不可用，所以固定使用yoursecret充当签名
var MySecret = []byte("yoursecret")

// GenerateToken 登录成功后调用，传入UserInfo结构体
func GenerateToken(auth Auth) (string, error) {
	expirationTime := time.Now().Add(TokenExpireDuration) // 两个小时有效期
	claims := &MyClaims{
		User: auth,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(),
			Issuer:    "zhanyan.com",
		},
	}
	// 生成Token，指定签名算法和claims
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// 签名
	if tokenString, err := token.SignedString(MySecret); err != nil {
		return "", err
	} else {
		return tokenString, nil
	}
}

// ParseToken 第一种方法返回空claims，或者上面定义的MySecret
func ParseToken(tokenString string) (*MyClaims, error) {
	claims := &MyClaims{}
	_, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (interface{}, error) {
		return MySecret, nil
	})
	// 若token只是过期claims是有数据的，若token无法解析claims无数据
	return claims, err
}

// ParseToken2 第二种方法通过jwt.ParseWithClaims返回的Token结构体取出Claims结构体
func ParseToken2(tokenString string) (*MyClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(t *jwt.Token) (interface{}, error) {
		return MySecret, nil
	})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, errors.New("token无法解析")
}
